# -*- coding: utf-8 -*-
# @Time    : 2018/2/5 13:55
# @Author  : Soft
# @File    : auth.py
from functools import wraps
from config import *
from flask import session, request, jsonify, redirect
import base64

log = logging.getLogger('AuthUtils LOG')

# 4.1修改
# Cookie验证改为session验证 解决长时间登录后无法加载记录信息问题
# 服务器操作课程考勤的验证
def auth_required(func):
    @wraps(func)
    def role(*args, **param):
        auth = LoginAuth.decrypt(session.get(AUTH_KEY, 'null+'))
        if auth != AUTH_VAL:
            return jsonify(status=False, msg=AUTH_ERR)
        return func(*args, **param)
    return role


class LoginAuth:
    __S_KEY = 'SESSION_KEY'
    __C_KEY = 'COOKIE_KEY'
    __ERR = 'Access denied'
    __get_user = None  # 函数 传入id 返回两个值 从数据库中查到的username和password
    __split = '__soft__'  # 用户密码与在数据库中的id间的间隔
    login_view_url = '/login'  # 用户验证失败后重定向的url

    def __init__(self, s_key=None, c_key=None):
        """
        初始化
        :param s_key: session的键
        :param c_key: cookie的键
        """
        if s_key:
            self.__S_KEY = s_key
        if c_key:
            self.__C_KEY = c_key

    def login(self, response, uid=0, username='', pass_wd='', remember=False):
        """
        保存回话状态
        :param response: Response实例 必须
        :param uid: 登录用户的id
        :param username: 用户名
        :param pass_wd:  密码
        :param remember: 是否记住密码
        :return: 响应Response
        """
        info = username + '_' + pass_wd + self.__split + str(uid)
        session[self.__S_KEY] = LoginAuth.encrypt(info)
        session[AUTH_KEY] = LoginAuth.encrypt(AUTH_VAL)  # 服务器获取信息接口认证
        if remember:
            c_val = LoginAuth.encrypt(info)
            response.set_cookie(self.__C_KEY, c_val, max_age=7 * 3600 * 24)
        return response

    def logout(self, response):
        """
        清理登录信息
        :param response: Response实例 必须
        :return: 该Response实例
        """
        response.delete_cookie(self.__C_KEY)
        response.delete_cookie(AUTH_KEY)  # 删除服务器认证
        if session.get(self.__S_KEY) is not None:
            session.pop(self.__S_KEY)
        return response

    def loader(self, func):
        """
        载入通过id从数据库获取用户的回调函数 必须是静态方法
        :param func: 回调函数
        """
        self.__get_user = func
        return func

    def required(self, func):
        """
        登录权限控制器 可以自动从cookie中加载用户信息保存在session中
        :param func: 被装饰的flask路由
        :return: 视图
        """
        @wraps(func)
        def view_(*args, **params):
            s_bool = False
            if session.get(self.__S_KEY) is not None:
                s_val = LoginAuth.decrypt(session.get(self.__S_KEY, 'null+'))
                if s_val.find(self.__split) != -1:
                    val, uid = s_val.split(self.__split)
                    if val == "_".join(self.__get_user(uid)):
                        s_bool = True
            if not s_bool and not self.auth_cookie():
                if request.method == 'POST':
                    return jsonify(status=False, error=self.__ERR)
                elif request.method == 'GET':
                    # 使用全局url的回滚
                    redirect_url = self.login_view_url + "?prev=" + request.base_url[:-1]
                    return redirect(redirect_url)
            # 验证成功后 判断session中是否有服务器验证，若没有则添加
            if session.get(AUTH_KEY) is not None:
                session[AUTH_KEY] = LoginAuth.encrypt(AUTH_VAL)  # 服务器获取信息接口认证
            return func(*args, **params)
        return view_

    def auth_cookie(self):
        """
        验证cookie中是否有用户登录信息
        :return: True or False
        """
        cs_val = LoginAuth.decrypt(request.cookies.get(self.__C_KEY, 'null+'))
        if cs_val.find(self.__split) == -1:
            return False
        c_val, uid = cs_val.split(self.__split)
        if c_val == "_".join(self.__get_user(uid)):
            session[self.__S_KEY] = LoginAuth.encrypt(cs_val)  # 从cookie中将用户存入session
            return True
        return False

    def get_user_id(self):
        """
        在session中获取用户id
        :return: 用户id或0
        """
        uid = 0
        try:
            uid = LoginAuth.decrypt(session.get(self.__S_KEY, 'null+')).split(self.__split)[1]
        except BaseException as e:
            log.debug('获取用户id错误 --> %s' % e)
        return uid

    def get_user_name(self):
        """
        从session中获取用户名
        :return: 用户名或空串
        """
        name = ''
        try:
            name = LoginAuth.decrypt(session.get(self.__S_KEY, 'null+')).split('_')[0]
        except BaseException as e:
            log.debug('获取用户名失败 --> %s' % e)
        return name

    @staticmethod
    def encrypt(string, key=12):
        """
        字符串加密
        :param string: 要加密的字符串
        :param key: 加密依赖
        :return: 加密字符串
        """
        return base64.b64encode(string.encode())

    @staticmethod
    def decrypt(string, key=12):
        """
        字符串解密
        :param string: 要解密的字符串
        :param key: 解密依赖
        :return: 解密后的字符串
        """
        try:
            bs = base64.b64decode(string).decode()
        except BaseException as e:
            log.debug('解密错误 --> %s' % e)
            return string

        return bs
